#include "coloured.h"
#include "helper.h"
#include <string>

Coloured::Coloured() : vertexLayout(0), mesh(0), effect(0), effectTechnique(0), WorldVariable(0), ViewVariable(0), ProjectionVariable(0) {
}

Coloured::~Coloured() {
}

bool Coloured::InitialiseModels(ID3D10Device *device) {
    // shaders
    if (!InitialiseShader(device))
        return false;

    // create scene objects
    if (!InitialiseGeometry(device))
        return false;

    return true;
}

void Coloured::Cleanup() {
    if (mesh != NULL) {
        mesh->Release();
        mesh = NULL;
    }

    if (effect != NULL) {
        effect->Release();
        effect = NULL;
    }

    effectTechnique = NULL;
    WorldVariable = NULL;
    ViewVariable = NULL;
    ProjectionVariable = NULL;
}

void Coloured::Render(ID3D10Device *device) {
    device->IASetInputLayout(vertexLayout);
	D3D10_TECHNIQUE_DESC techDesc;
	effectTechnique->GetDesc(&techDesc);
				//draw cube
	for (UINT p = 0; p < techDesc.Passes; p++)
	{
				//apply technique
				effectTechnique->GetPassByIndex(p)->Apply(0);
				mesh->DrawSubset(0);
	}
}

void Coloured::SetShaderVars(D3DXMATRIX World, D3DXMATRIX View, D3DXMATRIX Projection) {
    WorldVariable->SetMatrix(World);
    ViewVariable->SetMatrix(View);
    ProjectionVariable->SetMatrix(Projection);
}

bool Coloured::InitialiseGeometry(ID3D10Device *device) {
    pc::Vertex v[] ={pc::CreateVertex(D3DXVECTOR3(-1, 1, -1), D3DXVECTOR4(1, 0, 0,
        1)), pc::CreateVertex(D3DXVECTOR3(1, 1, -1),
        D3DXVECTOR4(0, 1, 0, 1)), pc::CreateVertex(D3DXVECTOR3(
        -1, -1, -1), D3DXVECTOR4(0, 0, 1, 1)),
        pc::CreateVertex(D3DXVECTOR3(1, -1, -1), D3DXVECTOR4(1,
        1, 0, 1)), pc::CreateVertex(D3DXVECTOR3(-1, 1,
        1), D3DXVECTOR4(1, 0, 0, 1)), pc::CreateVertex(
        D3DXVECTOR3(1, 1, 1), D3DXVECTOR4(0, 1, 0, 1)),
        pc::CreateVertex(D3DXVECTOR3(-1, -1, 1), D3DXVECTOR4(0,
        0, 1, 1)), pc::CreateVertex(D3DXVECTOR3(1, -1,
        1), D3DXVECTOR4(1, 1, 0, 1)),};

    UINT i[] ={2, 0, 3, 3, 1, 0, 3, 1, 7, 7, 5, 1, 6, 4, 2, 2, 0, 4, 7, 5, 6, 6, 4,
        5, 0, 4, 1, 1, 5, 4, 6, 2, 7, 7, 3, 2};

    // create a mesh
    HRESULT hr = D3DX10CreateMesh(device, pc::layout, pc::numElements,
            pc::layout[0].SemanticName, ARRAYSIZE(v), ARRAYSIZE(i) / 3,
            D3DX10_MESH_32_BIT, &mesh);

    if (FAILED(hr))
        return Error(L"Could not create a mesh");

    mesh->SetVertexData(0, v);
    mesh->SetIndexData(i, ARRAYSIZE(i));
    mesh->CommitToDevice();

    return true;
}

bool Coloured::InitialiseShader(ID3D10Device *device) {
    HRESULT hr = S_OK;
    ID3D10Blob *pErrors = NULL;

    hr = D3DX10CreateEffectFromFile(L"shaders.fx", NULL, NULL, "fx_4_0",
            D3D10_SHADER_ENABLE_STRICTNESS, 0, device, 0, 0, &effect, &pErrors,
            &hr);

    if (FAILED(hr)) {
        if (pErrors) {
            LPVOID compileErrors = pErrors->GetBufferPointer();
            using namespace std;
            string strErr = (const char*) compileErrors;
            wstring wcstrErr(strErr.begin(), strErr.end());
            return Error((WCHAR*) wcstrErr.c_str(), L"Shader Compile Errors.");
        } else {
            return Error(L"Shader file missing!");
        }
    }

    effectTechnique = effect->GetTechniqueByName("main");
    if (effectTechnique == NULL)
        return Error(L"Could not acquire the technique", L"Shader Error!");

    // obtain shader variables
    ObtainShaderVars();

    // create input layout
    if (!CreateInputLayout(device))
        return false;

    return true;
}

bool Coloured::CreateInputLayout(ID3D10Device *device) {
    using namespace pc;
    D3D10_PASS_DESC pDesc;
    effectTechnique->GetPassByIndex(0)->GetDesc(&pDesc);

    HRESULT hr = device->CreateInputLayout(layout, numElements,
            pDesc.pIAInputSignature, pDesc.IAInputSignatureSize, &vertexLayout);

    if (FAILED(hr))
        return Error(L"Could not create input layout");

    return true;
}

void Coloured::ObtainShaderVars() {
    WorldVariable = effect->GetVariableByName("World")->AsMatrix();
    ViewVariable = effect->GetVariableByName("View")->AsMatrix();
    ProjectionVariable = effect->GetVariableByName("Projection")->AsMatrix();
}
